from ccpm.pmbase import PluginBase, PmUtil
from ccpm.net import NetUtil
from ccpm.glan import Lan
from ccpm.conffac import GConf, PluginConf
import os
import zipfile
from ccpm.constants import Constant, Ptype
import json
import shutil
import sys
import copy
import re
from ccpm.collogger import Log
from mgamedev.py_lib.decorator import singleton
import oss2


def fmt_verstr(verstr):
    ext_v = 0
    if verstr.find("-") != -1:
        ext_v = re.findall("\d+", verstr.split("-")[1])[0]
        verstr = verstr.split("-")[0]

    nums = verstr[1:].split(".")
    int_ver = int(nums[0]) * 10 ** 6 + int(nums[1]) * \
        10 ** 4 + int(nums[2]) * 10 ** 2 + int(ext_v)
    return int_ver


def get_plugin_ilist(plugin_name):
    ilist = plugin_name.split("@")
    pbname = ilist[0]
    ver = ilist[1] if len(ilist) == 2 else None
    return (pbname, ver)


def _default_chkdir():
    pdir = os.path.join(os.getcwd(), Constant.L_PACKINSTALL_DIR)
    if not os.path.isdir(pdir):
        os.mkdir(pdir)
    return pdir


def _go_chkdir():
    ccpmname = Constant.L_PACKINSTALL_DIR.split("_")[0]
    pdir = os.path.join(
        os.getcwd(), ccpmname)
    if not os.path.isdir(pdir):
        os.mkdir(pdir)
    pack_i = 'package %s' % ccpmname
    packidx_f = os.path.join(pdir, "%s.go" % ccpmname)
    if not os.path.isfile(packidx_f):
        with open(packidx_f, "w") as fp:
            fp.write(pack_i)
    return pdir


HANDLE_MAP = {
    # defualt
    "default": _default_chkdir,
    Ptype.CODE_GO: _go_chkdir
}


@singleton
class InstallPlugin(PluginBase):

    PLUGIN_INFO = dict(
        name="install",
        help="install cocos package!"
    )

    _down_zipfiles = []

    def __init__(self):
        super().__init__(self.PLUGIN_INFO)

    def __choose_actpluginver(self, pinfolist, ver):
        if ver.find("+") == -1:
            for pinfo in pinfolist:
                if pinfo.get("ver") == "%s" % ver:
                    return pinfo
        else:
            if len(pinfolist) == 1:
                return pinfolist[0]
            else:
                (defidx, maxver) = 0, 0
                for pinfo in pinfolist:
                    defidx = pinfolist.index(pinfo) if fmt_verstr(
                        pinfo["ver"]) > maxver else defidx
                return pinfolist[defidx]

    def __chk_pluginurl(self, pinfo):
        """ get the main plugin name """
        host = PluginConf().get(self.name).static_addr
        static_path = PluginConf().get(self.name).static_path
        tpluginname = "%s@%s.zip" % (pinfo["name"], pinfo["ver"])
        url = "%s/%s/%s" % (host, static_path, tpluginname)
        return (url, tpluginname)

    def __chk_plugindir(self, ptype):
        print("ptype--->>", ptype)
        pname = Ptype.get_desc(ptype)
        print("-->>pname", pname)
        if pname in HANDLE_MAP:
            return HANDLE_MAP[pname]()
        return HANDLE_MAP["default"]()

    def __get_rel_plugininfo(self, pname, pver):
        pinfo_list = NetUtil.plugin_exist_chk(pname)
        if not pinfo_list:
            self.err_break(Lan.p(self.name, "ERR_NOPLUGIN_INREMOTE", pname))
        pinfo = self.__choose_actpluginver(pinfo_list, pver)
        if not pinfo:
            self.err_break(
                Lan.p(self.name, "ERR_NOPLUGINVER_INREMOTE", pname, pver))
        return pinfo

    def __chk_pluginstatu(self, inspath, pname, tver):
        path_plugin = os.path.join(inspath, pname)
        if os.path.isdir(path_plugin):
            dic_conf = json.load(
                open(os.path.join(path_plugin, Constant.PACKINFO_FILE)))
            if fmt_verstr(tver) <= fmt_verstr(dic_conf["ver"]):
                self.logger.info(
                    Lan.p(self.name, "INFO_ALREADY_NEWEXIST", pname, tver))
                return True
            else:
                self.logger.info(
                    Lan.p(self.name, "INFO_WILL_UPGRADE", pname,
                          dic_conf["ver"], pname, tver))
                shutil.rmtree(path_plugin)
        return False

    def __clean_downfiles(self):
        for fileobj in self._down_zipfiles:
            os.remove(fileobj)

    def __chk_denpencies(self, depenlist):
        for depp in depenlist:
            if len(depp) > 0:
                self.install_plugin(depp)

    def __get_relpinfo(self, pname, pver):
        if (not pver):
            self.err_break(Lan.p(self.name, "ERR_NOSPECIALVER", pname))
        self.logger.info("installing the plugin--->>><%s@%s>", pname, pver)
        pinfo = self.__get_rel_plugininfo(pname, pver)
        return pinfo

    def install_plugin(self, pluginname):
        # 提前检查是否安装完成
        # chk befor install
        (pname, pver) = get_plugin_ilist(pluginname)
        pinfo = self.__get_relpinfo(pname, pver)
        print(pinfo, "-->>>pinfo")
        ptype = pinfo["ptype"] if "ptype" in pinfo else 0
        inspath = self.__chk_plugindir(pinfo["ptype"])
        pdepen_list = pinfo["dependencies"].split(";")
        pdepen_listcnt = len(pdepen_list)
        ismain_exist = self.__chk_pluginstatu(
            inspath, pname, pinfo["ver"])
        if (ismain_exist and pdepen_listcnt > 0):
            self.__chk_denpencies(pdepen_list)
            return

        # download zip
        tfile = self.dl_packzip(inspath, pinfo)
        # unzip archive
        plugin_path = os.path.join(inspath, pinfo["name"])
        if os.path.isdir(plugin_path):
            shutil.rmtree(plugin_path)
        os.mkdir(plugin_path)
        azip = zipfile.ZipFile(tfile, "r", zipfile.ZIP_DEFLATED)
        for f in azip.namelist():
            azip.extract(f, plugin_path)
        # chk depenencies
        depen_conff = os.path.join(plugin_path, Constant.PACKINFO_FILE)
        InstallPlugin().__chk_denpencies(pinfo["dependencies"].split(";"))
        return tfile

    def parser_config(self, argparser):
        super().parser_config(argparser)
        argparser.add_argument("pluginname",
                               help="the plugin name to install")
        argparser.add_argument("-o", dest="isonlyzip",
                               help="Is just download the zip file?")

    def dl_packzip(self, inspath, pinfo, isdelzip=True):
        is_download_by_oss2 = GConf().get("is_oss2")
        tfile = None
        if is_download_by_oss2:
            tfile = self.__download_by_oss2(inspath, pinfo)
        else:
            tfile = self.__download_by_ftp(inspath, pinfo)
        if isdelzip:
            self._down_zipfiles.append(tfile)
        return tfile

    def __download_by_ftp(self, inspath, pinfo):
        (remote_url, zipf) = self.__chk_pluginurl(pinfo)
        # download
        tfile = os.path.join(inspath, zipf)
        NetUtil.ftp_download(remote_url, tfile)
        if not os.path.isfile(tfile):
            self.err_break(Lan.p(self.name, "ERR_DOWNLOAD_FAILED"))
        return tfile

    def __download_by_oss2(self, inspath, pinfo):
        (remote_url, zipf) = self.__chk_pluginurl(pinfo)

        ACCESS_KEY = PluginConf().get("_oss").oss2_accesskey
        ACCCESS_KEY_SECRET = PluginConf().get("_oss").oss2_secure
        REMOTE_PATH = PluginConf().get("_oss").oss_path
        OSS_DOWNLOAD_URL = PluginConf().get("_oss").oss_download_url
        OSS_DOWNLOAD_BUCKET = PluginConf().get("_oss").oss_bucket
        # 阿里云主账号AccessKey拥有所有API的访问权限，风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维，请登录 https://ram.console.aliyun.com 创建RAM账号。
        auth = oss2.Auth(ACCESS_KEY, ACCCESS_KEY_SECRET)
        oss2.logger.setLevel(30) # not import logging module
        # Endpoint以杭州为例，其它Region请按实际情况填写。
        bucket = oss2.Bucket(
            auth, OSS_DOWNLOAD_URL, OSS_DOWNLOAD_BUCKET)

        ret = NetUtil.oss2_download(
            bucket, os.path.join(REMOTE_PATH, zipf), zipf)
        return ret

    def run(self, argpars):
        super().run(argpars)
        isonlyzip = argpars.isonlyzip
        plugin_name = argpars.pluginname

        if not isonlyzip:
            # install full
            self.install_plugin(plugin_name)
        else:
            # install only zip
            (pname, pver) = get_plugin_ilist(plugin_name)
            pinfo = self.__get_relpinfo(pname, pver)
            self.dl_packzip(os.getcwd(), pinfo, False)
        # remove all
        self.__clean_downfiles()


__all__ = [
    "InstallPlugin"
]
